//
// [Author] lg (https://github.com/lemos1235)
// [Date] 10/20/2022
//
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart';
import 'package:scanning_helper/main.dart';

class CameraView extends StatefulWidget {
  const CameraView({
    super.key,
    required this.onImage,
    this.onInitialize,
    this.builder,
  });

  final Function(InputImage inputImage, CameraController controller)? onImage;
  final Function(CameraController controller)? onInitialize;

  final Widget Function(BuildContext context, Widget cameraPreview, CameraController cameraController)?
  builder;

  @override
  State<CameraView> createState() => _CameraViewState();
}

class _CameraViewState extends State<CameraView> with WidgetsBindingObserver {
  CameraController? controller;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
      if (!mounted) {
        return;
      }
      try {
        CameraDescription cameraDescription =
        deviceCameras.firstWhere((element) => element.lensDirection == CameraLensDirection.back);
        onNewCameraInit(cameraDescription);
        print('camera is initialized');
      } on CameraException catch (e) {
        print('error: ${e.description}');
      }
    });
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    stopCameraStream();
    super.dispose();
  }

  void stopCameraStream() async {
    try {
      if (controller != null && controller!.value.isInitialized && controller!.value.isStreamingImages) {
        await controller!.stopImageStream();
      }
    } catch (_) {}
    await controller?.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    final CameraController? cameraController = controller;
    if (cameraController == null || !cameraController.value.isInitialized) {
      return;
    }
    if (state == AppLifecycleState.inactive) {
      print('camera is paused');
      // cameraController.dispose();
      cameraController.pausePreview();
    } else if (state == AppLifecycleState.resumed) {
      print('camera is resumed ${cameraController.description.name}');
      // onNewCameraInit(cameraController.description);
      cameraController.resumePreview();
    }
  }

  @override
  Widget build(BuildContext context) {
    if (controller?.value.isInitialized ?? false) {
      var scaleValue = MediaQuery.of(context).size.aspectRatio * controller!.value.aspectRatio;
      if (scaleValue < 1) {
        scaleValue = 1 / scaleValue;
      }
      final cameraPreview = Transform.scale(
        scale: scaleValue,
        child: Center(child: CameraPreview(controller!)),
      );
      return widget.builder!(context, cameraPreview, controller!);
    } else {
      return const SizedBox.shrink();
    }
  }

  Future<void> onNewCameraInit(CameraDescription cameraDescription) async {
    final CameraController? oldController = controller;
    if (oldController != null) {
      controller = null;
      try {
        if (oldController.value.isInitialized && oldController.value.isStreamingImages) {
          await oldController.stopImageStream();
        }
      } catch (_) {}
      await oldController.dispose();
    }

    final CameraController cameraController = CameraController(
      cameraDescription,
      ResolutionPreset.high,
      enableAudio: false,
      // imageFormatGroup: ImageFormatGroup.jpeg,
    );
    controller = cameraController;

    try {
      await cameraController.initialize();
      widget.onInitialize?.call(cameraController);
      if (cameraController.value.isInitialized) {
        cameraController.lockCaptureOrientation(DeviceOrientation.portraitUp);
        cameraController.startImageStream(_processCameraImage);
      }
    } on CameraException catch (e) {
      print('camera err: ${e.code}');
    }

    if (mounted) {
      setState(() {});
    }
  }

  Future _processCameraImage(CameraImage image) async {
    final WriteBuffer allBytes = WriteBuffer();
    for (final Plane plane in image.planes) {
      allBytes.putUint8List(plane.bytes);
    }
    final bytes = allBytes.done().buffer.asUint8List();
    final Size imageSize = Size(image.width.toDouble(), image.height.toDouble());
    final imageRotation = InputImageRotationValue.fromRawValue(controller!.description.sensorOrientation);
    if (imageRotation == null) return;
    final inputImageFormat = InputImageFormatValue.fromRawValue(image.format.raw);
    if (inputImageFormat == null) return;
    final planeData = image.planes.map(
          (Plane plane) {
        return InputImagePlaneMetadata(
          bytesPerRow: plane.bytesPerRow,
          height: plane.height,
          width: plane.width,
        );
      },
    ).toList();
    final inputImageData = InputImageData(
      size: imageSize,
      imageRotation: imageRotation,
      inputImageFormat: inputImageFormat,
      planeData: planeData,
    );
    final inputImage = InputImage.fromBytes(bytes: bytes, inputImageData: inputImageData);
    widget.onImage?.call(inputImage, controller!);
  }
}

